/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski   *
 *   g.latarowski@gmail.com   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

// gcommon
#include "gexception.h"

// local
#include "roi.h"

const char * NODE   = "node";
const char * NODE_X = "x";
const char * NODE_Y = "y";


// ===========================================================================
//                                   GRoi
// ===========================================================================


// ------------------------------ GRoi::GRoi() -------------------------------
GRoi::GRoi( QGraphicsItem * pParent, QGraphicsScene * pScene )
	: GGraph( pParent, pScene )
{
	QGraphicsItem::setCursor( QCursor( Qt::ArrowCursor ) );
	
	_color = Qt::gray;
	
	adjust();
}
// ---------------------------------------------------------------------------


// ------------------------------ GRoi::~GRoi() ------------------------------
GRoi::~GRoi()
{
		// delte all nodes and edges
	foreach( GNode * node, _nodes )
	{
		delete node;
	}
	foreach( GEdge * edge, _edges )
	{
		delete edge;
	}
}
// ---------------------------------------------------------------------------


// ---------------------------- GRoi::toXml() --------------------------------
QDomElement GRoi::toXml( QDomDocument & doc )const
{
	QDomElement roiElement = doc.createElement( this->name() );
	
		// write nodes 
	foreach( QPointF point, this->nodes() )
	{
			// create point
		QDomElement pointElement = doc.createElement( NODE );
		roiElement.appendChild( pointElement );
		
			// create pos x element
		QDomElement xElement = doc.createElement( NODE_X );
		pointElement.appendChild( xElement );
		QDomText xText = doc.createTextNode( QString::number( point.x() ) );
		xElement.appendChild( xText );
		
			// create pos y element
		QDomElement yElement = doc.createElement( NODE_Y );
		pointElement.appendChild( yElement );
		QDomText yText = doc.createTextNode( QString::number( point.y() ) );
		yElement.appendChild( yText );
	}
	
		// succes 
	return roiElement;
}
// ---------------------------------------------------------------------------


// ----------------------------- GRoi::fromXml() -----------------------------
void GRoi::fromXml( const QDomElement & element )
{
	QDomElement roiElement;
		// check if searched node isn't root 
	if( element.tagName() == this->name() )
	{
		roiElement = element;
	}
	else
	{
		roiElement = element.firstChildElement( this->name() );
	}
	
		// if there is no roi than throw an error
	if( roiElement.isNull() )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Can not find '%1' node." ).arg( this->name() ) );
	}

		// create polygon for proper nodes
	QPolygonF grabbedNodes;
	
	// ====================================================
	// parse first possible node 
	
		// search for nodes 
	QDomElement nodeElement = roiElement.firstChildElement( NODE );
		// if no node found than throw an error
	if( nodeElement.isNull() )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Can not find '%1' node." ).arg( NODE ) );
	}
		// parse X
	QDomElement xElement = nodeElement.firstChildElement( NODE_X );
		// parse Y
	QDomElement yElement = nodeElement.firstChildElement( NODE_Y );
		// if xElement and yElement were found then process its texts
	if( !xElement.isNull() && !yElement.isNull() )
	{
			// try to convert data
		bool okX, okY;
		double x = xElement.text().toDouble( &okX );
		double y = yElement.text().toDouble( &okY );
			// if ok than add hook to grabbedHooks 
		if( okX && okY )
		{
			grabbedNodes << QPointF( x, y );
		}
	}

	// ====================================================
	// parse next node
	while( !nodeElement.nextSiblingElement().isNull() )
	{
		// ------------------------------------------------
		// search for next node 
		nodeElement = nodeElement.nextSiblingElement();
			// parse X
		QDomElement xElement = nodeElement.firstChildElement( NODE_X );
			// if searching for x elemment failed then process next node 
		if( xElement.isNull() )
		{
			continue;
		}
			// parse Y
		QDomElement yElement = nodeElement.firstChildElement( NODE_Y );
			// if searching for y elemment failed then process next node
		if( yElement.isNull() )
		{
			continue;
		}
			// try to convert elements data
		bool okX, okY;
		double x = xElement.text().toDouble( &okX );
		double y = yElement.text().toDouble( &okY );
			// if ok than add hook to grabbedHooks 
		if( okX && okY )
		{
			grabbedNodes << QPointF( x, y );
		}
	}

		// check if at least 3 nodes where found, if no throw an error
	if( grabbedNodes.size() < 3 )
	{
		// NOTE Maybe throw
		return;
// 		throw GDatasetError( tr( "Not enough %1 nodes found required by %1." )
// 				.arg( NODE ).arg( this->name() )
// 							  );
	}

		// everything seems to be ok, set founded nodes 
	setNodes( grabbedNodes );
}
// ---------------------------------------------------------------------------


// ------------------------ GRoi::setNodes() ---------------------------------
void GRoi::setNodes( const QPolygonF & positions )
{
		// adjust Roi, called to update _boundingRect to sceneRect()
	adjust();
	
		// clear previous edges
	foreach( GEdge * edge, _edges )
	{
		delete edge;
	}
	_edges.clear();
	
		// clear previous nodes
	foreach( GNode * node, _nodes )
	{
		delete node;
	}
	_nodes.clear();
	
	foreach( QPointF position, positions )
	{
			// create new removable node
		GNode * node = new GNode( this, true );
		node->setToolTip( tr( "Move nodes around the scene to place Roi." ) );
			// set Z Value for node
		node->setZValue( zValue() + 0.02 );
			// set nodes position
		node->setPos( position );
			// add node to rest
		_nodes << node;
	}
	
	updateEdges();
	
		// adjust Roi
	adjust();
}
// ---------------------------------------------------------------------------


// ----------------------- GRoi::nodes() -------------------------------------
QPolygonF GRoi::nodes()const
{
	QPolygonF polygon;
	foreach( GNode * node, _nodes )
	{
		if( node )
		{
			polygon << node->pos();
		}
	}
	
	return polygon;
}
// ---------------------------------------------------------------------------


// ------------------------ GRoi::updateEdges() ------------------------------
void GRoi::updateEdges()
{
	// clear previous edges
	foreach( GEdge * edge, _edges )
	{
		delete edge;
	}
	_edges.clear();
	
	// create edges
	for( int i = 0; i < _nodes.size() - 1; i++ )
	{
		GEdge * edge = new GEdge( this, _nodes[i], _nodes[i+1] );
		edge->setToolTip( tr( "Doubleclick to create new node." ) );
		
			// set Z Value for node
		edge->setZValue( zValue() + 0.01 );
			// add edge to rest
		_edges << edge;
	}
		// create edge between last and first node
	GEdge * edge = new GEdge( this, _nodes[ _nodes.size() - 1 ], _nodes[0] );
		// set Z Value for node
	edge->setZValue( zValue() + 0.01 );
		// add edge to rest
	_edges << edge;
}
// ---------------------------------------------------------------------------


// ---------------------------- GRoi::boundingRect() -------------------------
QRectF GRoi::boundingRect() const
{
	return _boundingRect;
}
// ---------------------------------------------------------------------------


// ---------------------------- GRoi::setZValue() ----------------------------
void GRoi::setZValue( const qreal z )
{
		// change items z value
	QGraphicsItem::setZValue( z );
		// set for all nodes
	foreach( GNode * node, _nodes )
	{
		if( node )
		{
			node->setZValue( zValue() + 0.02 );
		}
	}
		// set for all edges
	foreach( GEdge * edge, _edges )
	{
		if( edge )
		{
			edge->setZValue( zValue() + 0.01 );
		}
	}
}
// ---------------------------------------------------------------------------


// ----------------------------- GRoi::paint() -------------------------------
void GRoi::paint( QPainter * pPainter,
				  const QStyleOptionGraphicsItem * pOption,
				  QWidget * widget )
{
	Q_UNUSED( pOption );
	Q_UNUSED( widget );
	
	pPainter->setPen( QPen( Qt::NoPen ) );
	pPainter->setBrush( QColor( 150, 150, 150, 125 ) );

		// paint roi region outer 
	QPainterPath pathRoi;
	pathRoi.addRect( _boundingRect );
	pathRoi.moveTo( _roi.first() );
	pathRoi.addPolygon( _roi );
	pathRoi.closeSubpath();
	
	pPainter->drawPath( pathRoi );
}
// ---------------------------------------------------------------------------


// --------------------------- GRoi::adjust() --------------------------------
void GRoi::adjust()
{
	if( scene() )
	{
		_boundingRect = scene()->sceneRect();
	}
	
	removeFromIndex();
	_roi.clear();
	foreach( GNode * node, _nodes )
	{
		if( node )
		{
			_roi << node->pos();
		}
	}
	addToIndex();
	
		// adjust edges
	foreach( GEdge * edge, _edges )
	{
		if( edge )
		{
			edge->adjust();
		}
	}
	
		// say item changed
	emit itemChanged();
	
	update();
}
// ---------------------------------------------------------------------------


// --------------------------- GRoi::removeNode() ----------------------------
void GRoi::removeNode( GNode * node )
{
	if( node )
	{
		int index = _nodes.indexOf( node );
		_nodes.remove( index );
		delete node;
	}
	updateEdges();
	
		// prevent removing to less than 3 nodes, set nodes flag
	if( _nodes.size() < 4 )
	{
		foreach( GNode * node, _nodes )
		{
			node->setRemovable( false );
		}
	}
	
	adjust();
}
// ---------------------------------------------------------------------------


// ------------------------- GRoi::addNode() ---------------------------------
void GRoi::addNode( GNode * previousNode, const QPointF & position )
{
	if( previousNode )
	{
			// create new removable node
		GNode * node = new GNode( this );
			// set Z Value for node
		node->setZValue( zValue() + 0.02 );
			// set nodes position
		node->setPos( position );
			// add node to scene
		if( scene() )
		{
			scene()->addItem( node );
		}
			// add node to rest, after previousNode
		int index = _nodes.indexOf( previousNode ) + 1;
		_nodes.insert( index, node );
		
		updateEdges();
		
			// update nodes removable flag
		if( _nodes.size() > 3 )
		{
			foreach( GNode * node, _nodes )
			{
				node->setRemovable( true );
			}
		}
		else
		{
			foreach( GNode * node, _nodes )
			{
				node->setRemovable( false );
			}
		}
		
		adjust();
	}
}
// ---------------------------------------------------------------------------


// ------------------------- GRoi::roiRegion() -------------------------------
QPainterPath GRoi::roiRegion()const
{
	QPainterPath pathRoi;
	pathRoi.moveTo( _roi.first() );
	pathRoi.addPolygon( _roi );
	pathRoi.closeSubpath();
	
	return pathRoi;
}
// ---------------------------------------------------------------------------



// eof
